home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / FRAMEWIN.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  31.9 KB  |  1,241 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1991, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.44  $
  6. //
  7. // Implementation of class TFrameWindow, a TWindow with additional features
  8. // for frames, such as client window, menus, icons, etc.
  9. //----------------------------------------------------------------------------
  10. #pragma hdrignore SECTION
  11. #include <owl/pch.h>
  12. #include <owl/docking.h>
  13. #if !defined(OWL_FRAMEWIN_H)
  14. # include <owl/framewin.h>
  15. #endif
  16. #if !defined(OWL_APPLICAT_H)
  17. # include <owl/applicat.h>
  18. #endif
  19. #if !defined(OWL_MENU_H)
  20. # include <owl/menu.h>
  21. #endif
  22. #if !defined(WINSYS_UIMETRIC_H)
  23. # include <winsys/uimetric.h>
  24. #endif
  25. #include <stdio.h>
  26.  
  27. #if !defined(WM_SETICON)
  28. # define WM_SETICON 0x0080
  29. #endif
  30.  
  31. OWL_DIAGINFO;
  32. DIAG_DECLARE_GROUP(OwlCmd);
  33.  
  34. #if !defined(SECTION) || SECTION == 1
  35.  
  36. DEFINE_RESPONSE_TABLE1(TFrameWindow, TWindow)
  37.   EV_WM_PAINT,
  38.   EV_WM_ERASEBKGND,
  39.   EV_WM_QUERYDRAGICON,
  40.   EV_WM_INITMENUPOPUP,
  41.   EV_WM_SETFOCUS,
  42.   EV_WM_SIZE,
  43.   EV_WM_PARENTNOTIFY,
  44.   EV_WM_QUERYNEWPALETTE,
  45.   EV_WM_PALETTECHANGED,
  46. END_RESPONSE_TABLE;
  47.  
  48. //
  49. // Enable/disable the command sender, in this case the menu item.
  50. //
  51. void
  52. TMenuItemEnabler::Enable(bool enable)
  53. {
  54.   TCommandEnabler::Enable(enable);
  55.   ::EnableMenuItem(HMenu, Position,
  56.                    MF_BYPOSITION | (enable ? MF_ENABLED : MF_GRAYED));
  57. }
  58.  
  59. //
  60. // Set the text for the command sender, in this case the menu item.
  61. //
  62. void
  63. TMenuItemEnabler::SetText(const char far* str)
  64. {
  65.   ::ModifyMenu(HMenu, Position, MF_BYPOSITION | MF_STRING, Id, str);
  66. }
  67.  
  68. //
  69. // Set the check state of the command sender, in this case the menu item.
  70. //
  71. void
  72. TMenuItemEnabler::SetCheck(int state)
  73. {
  74.   ::CheckMenuItem(HMenu, Position,
  75.                   MF_BYPOSITION | (state == Checked ? MF_CHECKED : MF_UNCHECKED));
  76. }
  77.  
  78. //----------------------------------------------------------------------------
  79.  
  80. //
  81. // Constructor for a TFrameWindow
  82. //
  83. TFrameWindow::TFrameWindow(TWindow*        parent,
  84.                            const char far* title,
  85.                            TWindow*        clientWnd,
  86.                            bool            shrinkToClient,
  87.                            TModule*        module)
  88. {
  89.   // Initialize virtual base, in case the derived-most used default ctor
  90.   //
  91.   TWindow::Init(parent, title, module);
  92.  
  93.  
  94.   IconResId = 0;  // remember that we still need to init
  95.   Init(clientWnd, shrinkToClient);
  96. }
  97.  
  98. //
  99. // Constructor for a TFrameWindow. This ctor is generally not used by derived
  100. // classes, only as generic alias to a framewindow-like HWND
  101. //
  102. TFrameWindow::TFrameWindow(HWND THandle, TModule* module)
  103. :
  104.   TWindow(THandle, module)
  105. {
  106.   Init(0);
  107. }
  108.  
  109. //
  110. // Protected constructor for use by immediate virtually derived classes.
  111. // Immediate derivitives must call Init() before constructions are done.
  112. //
  113. TFrameWindow::TFrameWindow()
  114. {
  115.   IconResId = 0;  // Zero this member to remember that we still need to init
  116. }
  117.  
  118. //
  119. // Normal initialization of a default constructed TFrameWindow. Is ignored
  120. // if called more than once.
  121. //
  122. void
  123. TFrameWindow::Init(TWindow* clientWnd, bool shrinkToClient)
  124. {
  125.   if (!IconResId) {
  126.     Attr.Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
  127.     Attr.X = Attr.W = CW_USEDEFAULT;
  128.  
  129.     if (clientWnd)
  130.       Attr.Style |= WS_CLIPCHILDREN;
  131.  
  132.     if (shrinkToClient)
  133.       SetFlag(wfShrinkToClient);
  134.  
  135.     Init(clientWnd);
  136.   }
  137. }
  138.  
  139. //
  140. // Private initializer does a bulk of the common frame window initialization
  141. //
  142. void
  143. TFrameWindow::Init(TWindow* clientWnd)
  144. {
  145.   HWndRestoreFocus = 0;
  146.   KeyboardHandling = false;
  147.   ClientWnd        = clientWnd;
  148.  
  149.   MenuDescr   = 0;
  150.   MergeModule = 0;
  151.  
  152.   CurIcon      = 0;
  153.   CurIconSm    = 0;
  154.   IconModule   = 0;
  155.   IconSmModule = 0;
  156.   SetIcon(::Module, IDI_OWLAPP);
  157.   SetIconSm(::Module, IDI_OWLAPPSM);
  158.  
  159.   MinimizedPos = TPoint(-1,-1);  // Windows convention for never minimized
  160.  
  161.   if (ClientWnd) {
  162.     ClientWnd->SetParent(this);
  163.     ClientWnd->EnableAutoCreate();  // in case client is a dialog
  164.     SetBkgndColor(NoErase);         // no need to erase client area
  165.   }
  166. }
  167.  
  168. //
  169. // Destructor for a TFrameWindow
  170. //
  171. TFrameWindow::~TFrameWindow()
  172. {
  173.   if (IsFlagSet(wfMainWindow))
  174.     if (GetApplication()->GetMainWindow() == this)
  175.       GetApplication()->MainWindow = 0;
  176.  
  177.   delete MenuDescr;
  178. }
  179.  
  180. //
  181. // Return a state mask representing the enabled menu items (up to 32)
  182. //
  183. static uint32
  184. GetMenuStateBits(HMENU hmenu, int count)
  185. {
  186.   uint32 bit = 1;
  187.   uint32 result = 0;
  188.  
  189.   for (int i = 0; i < count; i++) {
  190.     uint state = GetMenuState(hmenu, i, MF_BYPOSITION);
  191.     if (state != (uint)-1) {
  192.       if (!(state  & (MF_DISABLED | MF_GRAYED))) {
  193.         result |= bit;
  194.       }
  195.     }
  196.     bit <<= 1;
  197.   }
  198.  
  199.   return result;
  200. }
  201.  
  202. //
  203. // Responds to WM_INITMENUPOPUP by performing a command enable run on each
  204. // of the menu items in the popup menu
  205. //
  206. void
  207. TFrameWindow::EvInitMenuPopup(HMENU hPopupMenu, uint index, bool sysMenu)
  208. {
  209.   if (!sysMenu && hPopupMenu) {
  210.     const int count = ::GetMenuItemCount(hPopupMenu);
  211.  
  212.     // Save current state of visible top level menus
  213.     //
  214.     uint32 preState = 0;
  215.     if (hPopupMenu == GetMenu())
  216.       preState = GetMenuStateBits(hPopupMenu, count);
  217.  
  218.     TWindow::EvInitMenuPopup(hPopupMenu, index, sysMenu);
  219.  
  220.     // If the top level menu state changes, redraw the menu bar
  221.     //
  222.     if (hPopupMenu == GetMenu())
  223.       if (GetMenuStateBits(hPopupMenu, count) != preState)
  224.         DrawMenuBar();
  225.   }
  226. }
  227.  
  228. //
  229. // TFrameWindow processes idle action occurs once per block of messages
  230. //
  231. bool
  232. TFrameWindow::IdleAction(long idleCount)
  233. {
  234.   if (idleCount == 0) {
  235.     // do command enabling for the menu bar if this is the active task
  236.     //
  237. #if defined(BI_PLAT_WIN32)
  238.     if (GetFocus()) {
  239. #else
  240.     HWND focus = GetFocus();
  241.     if (focus && ::GetWindowTask(focus) == GetCurrentTask()) {
  242. #endif
  243.       long style = ::GetWindowLong(*this, GWL_STYLE);
  244.       if (!(style & WS_CHILD)) {
  245.         if (IsWindow()) {
  246.           HMENU hMenu = ::GetMenu(*this);
  247.           if (IsMenu(hMenu))
  248.             HandleMessage(WM_INITMENUPOPUP, TParam1(hMenu));
  249.         }
  250.       }
  251.     }
  252.   }
  253.   // give child windows an opportunity to do any idle processing
  254.   //
  255.   return TWindow::IdleAction(idleCount);
  256. }
  257.  
  258. //
  259. // Locate and return the window that is the target of command and command
  260. // enable messages.
  261. //
  262. HWND
  263. TFrameWindow::GetCommandTarget()
  264. {
  265.   // Retrieve the focus window and our client
  266.   //
  267.   HWND hFocus = ::GetFocus();
  268.   TWindow* client = GetClientWindow();
  269.  
  270.   // 1. The first candidate is a focus window that's a child of our client
  271.   //
  272.   if (hFocus && client && client->IsChild(hFocus)) {
  273.     TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - focus, "\
  274.                       "child of client: " << hex << uint(hFocus));
  275.     return hFocus;
  276.   }
  277.  
  278.   // 2. The next option is our client window itself
  279.   //
  280.   if (client) {
  281.     TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - client: " << *client);
  282.     return *client;
  283.   }
  284.  
  285.   // 3. The next option is a focus window that's a child of ours
  286.   //
  287.   if (hFocus && IsChild(hFocus)) {
  288.     TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - focus, "\
  289.                        << hex << uint(hFocus));
  290.     return hFocus;
  291.   }
  292.  
  293.   // 4. If all of the above fail, resort to the last focus child of ours
  294.   //
  295.   if (HWndRestoreFocus) {
  296. #if defined(__TRACE) || defined(__WARN)
  297.     TWindow* win = GetWindowPtr(HWndRestoreFocus);
  298.     if (!win) {
  299.       TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - HwndRestoreFocus, "\
  300.                         << hex << uint(HWndRestoreFocus));
  301.     } else {
  302.       TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - HwndRestoreFocus, "\
  303.                         << *win);
  304.     }
  305. #endif
  306.     return HWndRestoreFocus;
  307.   }
  308.  
  309.   // 5. When all else fails, send ourselves in
  310.   //
  311.   TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - self, " << *this);
  312.   return *this;
  313. }
  314.  
  315. //
  316. // Handle WM_COMMAND to provide extra processing for commands:
  317. // Extra processing for commands: starts with the command target window
  318. // (usually the focus window) and gives it and its parent windows an
  319. // opportunity to handle the command.
  320. //
  321. TResult
  322. TFrameWindow::EvCommand(uint id, HWND hCtl, uint notifyCode)
  323. {
  324.   TRACEX(OwlCmd, 1, "TFrameWindow::EvCommand - id(" << id << "), ctl(" <<\
  325.                      hex << uint(hCtl) << "), code(" << notifyCode  << ")");
  326.  
  327.   // Walk the command chain from the command target back up to us or until
  328.   // we hit a child that is an owl window. Delegate to owl-child or forward to
  329.   // our base if no child is found.
  330.   //
  331.   if (hCtl == 0) {
  332.     HWND  hCmdTarget = GetCommandTarget();
  333.  
  334.     // Check owl parentage too in case the HWNDs were reparented
  335.     //
  336.     while (hCmdTarget && hCmdTarget != GetHandle()) {
  337.       TWindow*  cmdTarget = GetWindowPtr(hCmdTarget);
  338.  
  339.       if (cmdTarget)
  340.         return cmdTarget->EvCommand(id, hCtl, notifyCode);
  341.  
  342.       hCmdTarget = ::GetParent(hCmdTarget);
  343.     }
  344.   }
  345.  
  346.   return TWindow::EvCommand(id, hCtl, notifyCode);
  347. }
  348.  
  349. //
  350. // Handle WM_COMMAND_ENABLE to provide command enable distribution and default
  351. // support for windows without command enable handlers.
  352. //
  353. void
  354. TFrameWindow::EvCommandEnable(TCommandEnabler& commandEnabler)
  355. {
  356.   // Don't process for windows out of our window tree (esp. other apps)
  357.   //
  358.   RouteCommandEnable(GetCommandTarget(), commandEnabler);
  359. }
  360.  
  361. //
  362. // Preprocess queued messages to handle translation of accelerators on a per
  363. // window basis, and dialog-like keyboard navigation if enabled.
  364. //
  365. bool
  366. TFrameWindow::PreProcessMsg(MSG& msg)
  367. {
  368.   if (TWindow::PreProcessMsg(msg))
  369.     return true;  // Processed accelerators
  370.  
  371.   if (KeyboardHandling && msg.message >= WM_KEYFIRST &&
  372.                           msg.message <= WM_KEYLAST)
  373.   {
  374.     HWND parent = ::GetParent(msg.hwnd);
  375.  
  376.     // Retrieve the COMBO handle if we're in the EDIT ctrl parented to the
  377.     // combobox
  378.     //
  379.     char szClassName[0x10];
  380.     ::GetClassName(parent, szClassName, sizeof(szClassName));
  381.     if (!strcmpi(szClassName, "COMBOBOX"))
  382.       parent = ::GetParent(parent);
  383.  
  384.     if (parent && ::IsDialogMessage(parent, &msg))
  385.       return true;
  386.   }
  387.  
  388.   return false;
  389. }
  390.  
  391. //
  392. // Overrides TWindow's non-virtual SetMenu to make it virtual. This allows
  393. // derived frame classes to implement this differently
  394. // It also calls the application's PreProcessMenu() if it is the main window
  395. // to let it make any changes just before setting.
  396. //
  397. bool
  398. TFrameWindow::SetMenu(HMENU newMenu)
  399. {
  400.   if (IsFlagSet(wfMainWindow))
  401.     GetApplication()->PreProcessMenu(newMenu);
  402.  
  403.   return TWindow::SetMenu(newMenu);
  404. }
  405.  
  406. //
  407. // Perform a high-level menu assignment either before or after the HWND for the
  408. // window has been created.
  409. //
  410. // Returns true if successful; false otherwise
  411. //
  412. bool
  413. TFrameWindow::AssignMenu(TResId menuResId)
  414. {
  415.   if (menuResId != Attr.Menu) {
  416.     if (Attr.Menu.IsString())
  417.       delete (char far*)Attr.Menu;
  418.  
  419.     Attr.Menu = menuResId.IsString() ? strnewdup(menuResId) : (char far*)menuResId;
  420.   }
  421.  
  422.   // If the window has been created then load and set the new menu and destroy
  423.   // the old menu
  424.   //
  425.   if (!GetHandle())
  426.     return true;
  427.  
  428.   HMENU curMenu = GetMenu();
  429.   HMENU newMenu = GetModule()->LoadMenu(Attr.Menu);
  430.  
  431.   if (!SetMenu(newMenu))
  432.     return false;
  433.  
  434.   if (curMenu)
  435.     ::DestroyMenu(curMenu);
  436.  
  437.   return true;
  438. }
  439.  
  440. //
  441. // Set the Icon for use when this frame is minimized
  442. //
  443. bool
  444. TFrameWindow::SetIcon(TModule* module, TResId resId)
  445. {
  446.   // Delete old icon if not system icon
  447.   //
  448.   if (CurIcon && IconModule) {
  449.     TUser::DestroyIcon(CurIcon);
  450.     CurIcon = 0;
  451.   }
  452.  
  453.   IconModule = module;
  454.   IconResId = resId;
  455.  
  456.   HINSTANCE hInstance = IconModule ? HINSTANCE(*IconModule) : HINSTANCE(0);
  457.  
  458.   if (IconResId != 0)
  459.     CurIcon = TUser::LoadIcon(hInstance, IconResId);
  460.  
  461.   if (CurIcon && IsWindow())
  462.     SendMessage(WM_SETICON, true, (LPARAM)(HICON)CurIcon);
  463.  
  464.   return true;
  465. }
  466.  
  467. //
  468. // Set the Small Icon (16 x 16)
  469. //
  470. bool
  471. TFrameWindow::SetIconSm(TModule* module, TResId resId)
  472. {
  473.   // Only proceed if the system supports small icons
  474.   //
  475.   if (!TSystem::HasSmallIcon())
  476.     return false;
  477.  
  478.   // Delete old small icon
  479.   //
  480.   if (CurIconSm && IconSmModule) {
  481.     TUser::DestroyIcon(CurIconSm);
  482.     CurIconSm = 0;
  483.   }
  484.  
  485.   IconSmModule = module;
  486.   IconSmResId  = resId;
  487.  
  488.   HINSTANCE hInstance = IconSmModule ? HINSTANCE(*IconSmModule) : HINSTANCE(0);
  489.   if (IconSmResId != 0) {
  490. #if defined(BI_PLAT_WIN32)
  491.     CurIconSm = (HICON)::LoadImage(hInstance, IconSmResId, IMAGE_ICON,
  492.                                    TUIMetric::CxSmIcon, TUIMetric::CySmIcon,
  493.                                    LR_DEFAULTCOLOR);
  494.     if (!CurIconSm)
  495.       CurIconSm = TUser::LoadIcon(hInstance, IconSmResId);
  496. #else
  497.     CurIconSm = TUser::LoadIcon(hInstance, IconSmResId);
  498. #endif
  499.   }
  500.  
  501.   if (CurIconSm && IsWindow())
  502.     SendMessage(WM_SETICON, false, (LPARAM)(HICON)CurIconSm);
  503.  
  504.   return true;
  505. }
  506.  
  507. //
  508. // Return the current client window as a TWindow*
  509. //
  510. TWindow*
  511. TFrameWindow::GetClientWindow()
  512. {
  513.   return ClientWnd;
  514. }
  515.  
  516. //
  517. // Remove the current client (if any) and set a new one.
  518. // Assume clientWnd was parented to us.
  519. //
  520. TWindow*
  521. TFrameWindow::SetClientWindow(TWindow* clientWnd)
  522. {
  523.   TWindow* oldClientWnd = ClientWnd;
  524.   HWND oldWnd = oldClientWnd ? oldClientWnd->GetHandle() : (HWND)0;
  525.   RemoveChild(ClientWnd);
  526.  
  527.   if (HWndRestoreFocus == oldWnd)
  528.     HWndRestoreFocus = 0;
  529.  
  530.   ClientWnd = clientWnd;
  531.  
  532.   if (ClientWnd) {
  533.     ClientWnd->SetParent(this);
  534.     if (GetHandle()) {
  535.       if (!ClientWnd->GetHandle())
  536.         ClientWnd->Create();
  537.       ClientWnd->ShowWindow(SW_SHOWNOACTIVATE);
  538.     }
  539.     SetBkgndColor(NoErase);         // no need to erase client area
  540.     ResizeClientWindow(true);       
  541.   }
  542.   else
  543.     SetBkgndColor(NoColor);         // will need to erase client area
  544.  
  545.   // Pass the focus to the new client, but only if we have it currently
  546.   //
  547.   if (ClientWnd && ClientWnd->GetHandle() && GetFocus() == GetHandle()) {
  548.     ClientWnd->SetFocus();
  549.     HWndRestoreFocus = ClientWnd->GetHandle();
  550.   }
  551.  
  552.   return oldClientWnd;
  553. }
  554.  
  555. //
  556. // If someone removes our client with a RemoveChild() call, update our client
  557. // and restore focus ptrs.
  558. //
  559. void
  560. TFrameWindow::RemoveChild(TWindow* child)
  561. {
  562.   TWindow::RemoveChild(child);
  563.   if (child) {
  564.     if (child == ClientWnd)
  565.       ClientWnd = 0;
  566.     if (child->GetHandle() == HWndRestoreFocus) {
  567.       HWndRestoreFocus = 0;
  568.     }
  569.   }
  570. }
  571.  
  572. //
  573. // Set the document style title for this frame window. Uses SetWindowText to
  574. // modify the caption directly without modifying the Title data member.
  575. // Generates a composite title based on Title if it exists, docname, and index
  576. // if it is > 0.
  577. //
  578. //  [<Title> - ]<docname>[:<index>]
  579. //
  580. bool
  581. TFrameWindow::SetDocTitle(const char far* docname, int index)
  582. {
  583.   if (index >= 0) {
  584.     string title;
  585.  
  586.     if (Title && *Title) {
  587.       title = Title;
  588.       title += " - ";
  589.     }
  590.  
  591.     if (docname)
  592.       title += docname;
  593.  
  594.     if (index > 0) {
  595.       title += ':';
  596.       char num[10];
  597.       itoa(index, num, 10);
  598.       title += num;
  599.     }
  600.  
  601.     SetWindowText(title.c_str());
  602.   }  // else if index negative, simply acknowledge that title will display
  603.   return true;
  604. }
  605.  
  606. //
  607. // Obtain the real windows application icon. The IDI_APPLICATION icon is an
  608. // ugly black & white box, but when a class is registered with this icon it
  609. // gets substituted with a better windows icon. Worse case we end up with the
  610. // ugly box icon.
  611. //
  612. static HICON
  613. getAppIcon()
  614. {
  615.   static HICON hRealAppIcon = 0;
  616.   if (!hRealAppIcon) {
  617.     WNDCLASS wndClass;
  618.     static char className[] = "IconSnarfer";
  619.     memset(&wndClass, 0, sizeof wndClass);
  620.     wndClass.hInstance = *::Module;
  621.     wndClass.hIcon = ::LoadIcon(0, IDI_APPLICATION);
  622.     wndClass.lpszClassName = className;
  623.     wndClass.lpfnWndProc = ::DefWindowProc;
  624.     ::RegisterClass(&wndClass);
  625.     ::GetClassInfo(*::Module, className, &wndClass);
  626.     hRealAppIcon = wndClass.hIcon;
  627.     ::UnregisterClass(className, *::Module);
  628.   }
  629.   return hRealAppIcon ? hRealAppIcon : ::LoadIcon(0, IDI_APPLICATION);
  630. }
  631.  
  632. //
  633. // Response method for an incoming WM_PAINT message
  634. //
  635. // If iconic, and an icon has been defined then draw that.
  636. // Or, if iconic & there is a client window, then call its paint function.
  637. //
  638. // If not iconic, forwards to TWindow for normal paint processing
  639. //
  640. void
  641. TFrameWindow::EvPaint()
  642. {
  643.   if (IsIconic() && (IconResId || ClientWnd)) {
  644.     TPaintDC  dc(GetHandle());
  645.  
  646.     if (IconResId) {
  647.       SendMessage(WM_ICONERASEBKGND, TParam1(HDC(dc)));
  648.       ::DrawIcon(dc, 0, 0, CurIcon ? CurIcon : getAppIcon());
  649.     }
  650.     else
  651.       ClientWnd->Paint(dc, dc.Ps.fErase, *(TRect*)&dc.Ps.rcPaint);
  652.   }
  653.   else
  654.     TWindow::EvPaint();
  655. }
  656.  
  657. //
  658. // Response method for an incoming WM_ERASEBKGND message
  659. //
  660. // If this frame window is iconic, and there is a client window, then give it
  661. // a chance to erase the background since it may want to take over painting.
  662. //
  663. // If not iconic, forward to TWindow for normal erase background processing
  664. //
  665. bool
  666. TFrameWindow::EvEraseBkgnd(HDC hDC)
  667. {
  668.   if (IsIconic()) {
  669.     if (!IconResId && ClientWnd)
  670.       return (bool)ClientWnd->HandleMessage(WM_ERASEBKGND, TParam1(hDC));
  671.  
  672.     HandleMessage(WM_ICONERASEBKGND, TParam1(hDC));
  673.     return true;
  674.   }
  675.   else
  676.     return TWindow::EvEraseBkgnd(hDC);
  677. }
  678.  
  679. //
  680. // Response method for an incoming WM_QUERYDRAGICON message
  681. //
  682. // If there is an icon set for this frame, then return it so that windows
  683. // can make a nice cursor out of it when the user drags the icon
  684. //
  685. HANDLE
  686. TFrameWindow::EvQueryDragIcon()
  687. {
  688.   if (IconResId) {
  689.     HINSTANCE hInstance = IconModule ? HINSTANCE(*IconModule) : HINSTANCE(0);
  690.     HICON hIcon = TUser::LoadIcon(hInstance, IconResId);
  691.     return hIcon ? hIcon : getAppIcon();
  692.   }
  693.   else
  694.     return TWindow::EvQueryDragIcon();
  695. }
  696.  
  697. static inline bool
  698. IsEnabledVisibleChild(long style)
  699. {
  700.   return (style & (WS_CHILD | WS_VISIBLE | WS_DISABLED)) == (WS_CHILD | WS_VISIBLE);
  701. }
  702.  
  703. static TWindow*
  704. SearchForChildWithTab(TWindow* win)
  705. {
  706.   TWindow*  firstChild = win->GetFirstChild();
  707.  
  708.   if (firstChild) {
  709.     TWindow*  child = firstChild;
  710.  
  711.     do {
  712.       if (child->GetHandle()) {
  713.         long  style = child->GetWindowLong(GWL_STYLE);
  714.  
  715.         if (IsEnabledVisibleChild(style)) {
  716.           if (style & WS_TABSTOP)
  717.             return child;
  718.  
  719.           else {
  720.             TWindow*  result = SearchForChildWithTab(child);
  721.             if (result)
  722.               return result;
  723.           }
  724.         }
  725.       }
  726.       child = child->Next();
  727.     } while (child != firstChild);
  728.   }
  729.  
  730.   return 0;
  731. }
  732.  
  733. static bool
  734. EnabledVisibleChild(TWindow* win, void*)
  735. {
  736.   return win->GetHandle() ? IsEnabledVisibleChild(win->GetWindowLong(GWL_STYLE)) :
  737.                         false;
  738. }
  739.  
  740. //
  741. // If the receiver doesn't have any children then returns 0. Otherwise
  742. // we search for the first child with WS_TABSTOP; If no child has WS_TABSTOP
  743. // then we return the first enabled visible child
  744. //
  745. // Does a depth-first search of nested child windows
  746. //
  747. // NOTE: we stop at the first child with WS_TABSTOP and do not search its
  748. //       children...
  749. //
  750. TWindow*
  751. TFrameWindow::FirstChildWithTab()
  752. {
  753.   TWindow*  win = SearchForChildWithTab(this);
  754.  
  755.   return win ? win : FirstThat(EnabledVisibleChild);
  756. }
  757.  
  758. //
  759. // Respond to a request to hold on to the handle of a child window that is
  760. // losing focus, so that we can restore it again later (below).
  761. //
  762. // return true if caller can stop searching for a window to hold its handle.
  763. //
  764. bool
  765. TFrameWindow::HoldFocusHWnd(HWND hWndLose, HWND hWndGain)
  766. {
  767.   if (IsChild(hWndLose)) {
  768.     if (!hWndGain || !IsChild(hWndGain))
  769.       HWndRestoreFocus = hWndLose;
  770.     return true;
  771.   }
  772.   return hWndLose == GetHandle();
  773. }
  774.  
  775. //
  776. // Handle WM_SETFOCUS to return focus back to the child that had it most
  777. // recently, or find the best one to give it to otherwise.
  778. //
  779. void
  780. TFrameWindow::EvSetFocus(HWND hWndLostFocus)
  781. {
  782.   TWindow::EvSetFocus(hWndLostFocus);
  783.  
  784.   if (!HWndRestoreFocus) {
  785.     HWND cmdTgt = GetCommandTarget();
  786.     if (cmdTgt && IsChild(cmdTgt))
  787.       HWndRestoreFocus = cmdTgt;
  788.   }
  789.  
  790. //  if (HWndRestoreFocus == hWndLostFocus)
  791. //    HWndRestoreFocus = GetHandle();
  792.  
  793.   if (HWndRestoreFocus) {
  794.     // Set focus to the saved HWND as long as it is still a valid window handle
  795.     //
  796.     if (::IsWindow(HWndRestoreFocus))
  797.       ::SetFocus(HWndRestoreFocus);
  798.     else
  799.       HWndRestoreFocus = 0;
  800.   }
  801. }
  802.  
  803. //
  804. // Close this window if the client is destroyed
  805. //
  806. // Clear the wfFullyCreated flag on any child that is destroyed
  807. // Resize this frame if the client changes size & wfShrinkToClient is set
  808. //
  809. void
  810. TFrameWindow::EvParentNotify(uint event,
  811.                              uint childHandleOrX, uint /*childIDOrY*/)
  812. {
  813.   if (event == WM_DESTROY) {
  814.     if (ClientWnd && ClientWnd->GetHandle() == HWND(childHandleOrX))
  815.       PostMessage(WM_CLOSE);  // using ShutDownWindow() has side effects
  816.  
  817.     TWindow* c = GetWindowPtr(HWND(childHandleOrX));
  818.     if (c)
  819.       c->ClearFlag(wfFullyCreated);
  820.   }
  821.   else if (event == WM_SIZE) {
  822.     if (IsFlagSet(wfShrinkToClient)
  823.         && ClientWnd
  824.         && ClientWnd->GetHandle() == HWND(childHandleOrX)
  825.         && !IsIconic())
  826.       ResizeClientWindow(true); 
  827.   }
  828.   DefaultProcessing();
  829. }
  830.  
  831. //
  832. // Forwards the WM_QUERYNEWPALETTE message to the client window.
  833. //
  834. bool
  835. TFrameWindow::EvQueryNewPalette()
  836. {
  837.   if (GetClientWindow())
  838.     return GetClientWindow()->ForwardMessage();
  839.   else
  840.     return TWindow::EvQueryNewPalette();
  841. }
  842.  
  843. //
  844. // Forwards the WM_PALETTECHANGED message to the client window.
  845. //
  846. void
  847. TFrameWindow::EvPaletteChanged(HWND hWndPalChg)
  848. {
  849.   if (GetClientWindow())
  850.     GetClientWindow()->ForwardMessage();
  851.   else
  852.     TWindow::EvPaletteChanged(hWndPalChg);
  853. }
  854.  
  855. //
  856. // Resize & reposition the client window to fit in this frames client area
  857. // or resize the frame to fit around the client's client area if
  858. // wfShrinkToClient
  859. // Return true if a client was actualy resized.
  860. // Adjust clients styles & make sure they get set.
  861. //
  862. bool
  863. TFrameWindow::ResizeClientWindow(bool repaint)
  864. {
  865.  
  866.   // Nothing to resize if there's not Client window
  867.   //
  868.   if (!ClientWnd)
  869.     return false;
  870.  
  871.   // Prevent recursion during resize by ignore calls from EvParentNotify and
  872.   // EvSize when we have already been called.
  873.   // Do this by disabling notifications while resizing using the
  874.   // wfShrinkToClient flag as a semaphore on the client
  875.   //
  876.   if (ClientWnd->IsFlagSet(wfShrinkToClient))
  877.     return true;
  878.   ClientWnd->SetFlag(wfShrinkToClient);
  879.  
  880.   bool  clientResized = false;
  881.   TSize clientAreaSize = GetClientRect().Size();
  882.   TSize childSize = ClientWnd->GetWindowRect().Size();
  883.  
  884.   // First time through, strip client window of thick borders.
  885.   // If shrink-to-client, then must measure the client size first
  886.   // If the client has scrolls bars, we must hide them to obtain the correct
  887.   // size.
  888.   // Border style is left on & dealt with by hand below
  889.   //
  890.   const uint32 badClientStyles = WS_DLGFRAME | WS_THICKFRAME | // bad borders
  891.                                  WS_POPUP | WS_OVERLAPPED;     // bad parenting
  892.   const uint32 badClientExStyles = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
  893.                                    WS_EX_STATICEDGE;      // more bad borders
  894.  
  895.   if ((ClientWnd->GetStyle() & badClientStyles) ||
  896.       (ClientWnd->GetExStyle() & badClientExStyles)) {
  897.     if (IsFlagSet(wfShrinkToClient)) {
  898.       TSize tstSize = ClientWnd->GetClientRect().Size();
  899.       ClientWnd->ShowScrollBar(SB_BOTH, false);
  900.       childSize = ClientWnd->GetClientRect().Size();
  901.       if (childSize != tstSize) {
  902.         int restore = SB_BOTH;
  903.         if (childSize.cx == tstSize.cx)
  904.           restore = SB_HORZ;
  905.         if (childSize.cy == tstSize.cy)
  906.           restore = SB_VERT;
  907.         ClientWnd->ShowScrollBar(restore, true);
  908.       }
  909.     }
  910.     if (ClientWnd->GetStyle() & badClientStyles) {
  911.       bool reparent = (ClientWnd->GetStyle() & (WS_POPUP|WS_OVERLAPPED)) != 0;
  912.       uint32 style = ClientWnd->GetStyle();
  913.       style &= ~badClientStyles;
  914.       style |= WS_CHILD | WS_BORDER | WS_VISIBLE;
  915.       ClientWnd->SetStyle( style );
  916.       if (reparent)
  917.         ::SetParent(*ClientWnd, *this);
  918.     }
  919.     if (ClientWnd->GetExStyle() & badClientExStyles) {
  920.       uint32 exStyle = ClientWnd->GetExStyle();
  921.       exStyle &= ~badClientExStyles;
  922.       ClientWnd->SetExStyle( exStyle );
  923.     }
  924.   }
  925.   if (ClientWnd->GetStyle() & WS_BORDER) {
  926.     childSize = ClientWnd->GetClientRect().Size();
  927.   }
  928.   if (childSize != clientAreaSize) {
  929.     if (IsFlagSet(wfShrinkToClient)) {
  930.       TRect outside = GetWindowRect();
  931.       TSize border = outside.Size() - clientAreaSize;
  932.       SetWindowPos(0, 0, 0,
  933.                    childSize.cx + border.cx, childSize.cy + border.cy,
  934.                    SWP_NOACTIVATE|SWP_NOMOVE| (repaint ? 0 : SWP_NOREDRAW));
  935.       clientAreaSize = childSize; // Must move client, will not cause an EvSize
  936.     }
  937.     else {
  938.       clientResized = true;       // Client will get resized
  939.     }
  940.   }
  941.   // If frame is sizeable, turn off flag so that user can then resize
  942.   // after initial setup
  943.   //
  944.   if (Attr.Style & WS_THICKFRAME && !TYPESAFE_DOWNCAST(this,TFloatingSlip) )
  945.     ClearFlag(wfShrinkToClient);
  946.  
  947.  
  948.   // Handle simple border style by shoving the client's borders under the frame
  949.   // This code MUST not resize the client if shrinkToClient
  950.   //
  951.   if (ClientWnd->GetStyle() & WS_BORDER) {
  952.     int bx = TUIMetric::CxBorder;
  953.     int by = TUIMetric::CyBorder;
  954.     ClientWnd->MoveWindow(-bx, -by, clientAreaSize.cx+bx+bx,
  955.                                     clientAreaSize.cy+by+by, repaint);
  956.   }
  957.   else
  958.     ClientWnd->MoveWindow(0, 0, clientAreaSize.cx, clientAreaSize.cy, repaint);
  959.  
  960.   // Turn off semaphore bit
  961.   //
  962.   ClientWnd->ClearFlag(wfShrinkToClient);
  963.   return clientResized;
  964. }
  965.  
  966. //
  967. // Called following a successful association between an MS-Windows interface
  968. // element and a TFrameWindow
  969. //
  970. void
  971. TFrameWindow::SetupWindow()
  972. {
  973.   // Create windows in child list (this includes the client window)
  974.   //
  975.   TWindow::SetupWindow();
  976.  
  977.   ResizeClientWindow(true); 
  978.  
  979.   if (MinimizedPos != TPoint(-1,-1)) {
  980.     WINDOWPLACEMENT windata;
  981.     windata.length = sizeof(WINDOWPLACEMENT);
  982.     GetWindowPlacement(&windata);
  983.     windata.flags = WPF_SETMINPOSITION;
  984.     windata.showCmd = SW_SHOWNA;
  985.     windata.ptMinPosition = MinimizedPos;
  986.     SetWindowPlacement(&windata);
  987.   }
  988.  
  989.   // If SetMenuDescr() was called before window created, update the menu now
  990.   //
  991.   if (IsFlagSet(wfMainWindow) && MenuDescr) {
  992.     HMENU curMenu = GetMenu();
  993.     TMenu newMenu(*MenuDescr, NoAutoDelete);
  994.     if (SetMenu(newMenu)) {
  995.       if (curMenu)
  996.         ::DestroyMenu(curMenu);
  997.     }
  998.     else
  999.       ::DestroyMenu(newMenu);
  1000.   }
  1001.  
  1002.   // If we haven't set THandleRestoreFocus then pick the first child with tabstop
  1003.   //
  1004.   if (!HWndRestoreFocus) {
  1005. #if 1
  1006.     TWindow*  win = FirstChildWithTab();
  1007.     HWndRestoreFocus = win ? win->GetHandle() : GetHandle();
  1008. #else
  1009.     HWND cmdTgt = GetCommandTarget();
  1010.     if (cmdTgt && IsChild(cmdTgt))
  1011.       HWndRestoreFocus = cmdTgt;
  1012. #endif
  1013.   }
  1014.  
  1015.   if (CurIcon)
  1016.     SendMessage(WM_SETICON, true, (LPARAM)(HICON)CurIcon);
  1017.  
  1018.   if (CurIconSm)
  1019.     SendMessage(WM_SETICON, false, (LPARAM)(HICON)CurIconSm);
  1020. }
  1021.  
  1022. //
  1023. // Cleans up any associated icons.
  1024. //
  1025. void
  1026. TFrameWindow::CleanupWindow()
  1027. {
  1028.   // icon cleanup
  1029.   //
  1030.   SetIcon(0, 0);
  1031.   SetIconSm(0, 0);
  1032.  
  1033.   TWindow::CleanupWindow();
  1034. }
  1035.  
  1036. //
  1037. // Tell child windows frame has minimized/maximized/restored
  1038. // (They may want to change enabled state or release/restore resources)
  1039. //
  1040. void
  1041. TFrameWindow::BroadcastResizeToChildren(uint sizeType, TSize& size)
  1042. {
  1043.   if (sizeType == SIZE_MINIMIZED
  1044.      || sizeType == SIZE_MAXIMIZED
  1045.      || sizeType == SIZE_RESTORED)
  1046.   {
  1047.     ChildBroadcastMessage(WM_OWLFRAMESIZE, TParam1(sizeType), TParam2(&size));
  1048.   }
  1049. }
  1050.  
  1051. //
  1052. // Response method for an incoming WM_SIZE message
  1053. //
  1054. // If not minimizing resizes the client window to be the same size as the
  1055. // client rectangle,
  1056. // If no WM_SIZE sent, forwards WM_SIZE message to client so it can recalc.
  1057. //
  1058. void
  1059. TFrameWindow::EvSize(uint sizeType, TSize& size)
  1060. {
  1061.   TWindow::EvSize(sizeType, size);
  1062.  
  1063.   if (ClientWnd) {
  1064.     bool sizeSent = false;
  1065.     if (sizeType != SIZE_MINIMIZED) {
  1066.       sizeSent = ResizeClientWindow(true); 
  1067.       size = ClientWnd->GetClientRect().Size();
  1068.     }
  1069.     if (!sizeSent)
  1070.       ClientWnd->ForwardMessage();
  1071.   }
  1072.  
  1073.   BroadcastResizeToChildren(sizeType, size);
  1074. }
  1075.  
  1076. #endif
  1077. #if !defined(SECTION) || SECTION == 2
  1078.  
  1079. //
  1080. // Set the menu descriptor for this frame window
  1081. //
  1082. void
  1083. TFrameWindow::SetMenuDescr(const TMenuDescr& menuDescr)
  1084. {
  1085.   delete MenuDescr;
  1086.   MenuDescr = new TMenuDescr(menuDescr);
  1087.  
  1088.   if (IsFlagSet(wfMainWindow) && GetHandle()) {
  1089.     HMENU curMenu = GetMenu();
  1090.     TMenu newMenu(*MenuDescr, NoAutoDelete);
  1091.     if (SetMenu(newMenu))
  1092.       ::DestroyMenu(curMenu);
  1093.     else
  1094.       ::DestroyMenu(newMenu);
  1095.   }
  1096. }
  1097.  
  1098. //
  1099. // Merge another menu, given its menu descriptor, into our own using our menu
  1100. // descriptor.
  1101. // optionally use an existing HMENU to merge into & set
  1102. //
  1103. bool
  1104. TFrameWindow::MergeMenu(const TMenuDescr& childMenuDescr)
  1105. {
  1106.   if (!MenuDescr || !GetHandle())
  1107.     return false;
  1108.  
  1109.   MergeModule = childMenuDescr.GetModule();
  1110.   TMenu  curMenu(*this, NoAutoDelete);
  1111.   TMenu  newMenu(NoAutoDelete);
  1112.  
  1113.   MenuDescr->Merge(childMenuDescr, newMenu);
  1114.  
  1115.   if (IsFlagSet(wfMainWindow))
  1116.     GetApplication()->PreProcessMenu(newMenu);
  1117.  
  1118.   if (SetMenu(newMenu)) {
  1119.     ::DestroyMenu(curMenu);
  1120.     return true;
  1121.  
  1122.   }
  1123.   else {
  1124.     ::DestroyMenu(newMenu);
  1125.     return false;
  1126.   }
  1127.  
  1128. }
  1129.  
  1130. //
  1131. // Restores this frame window's menu to the one described by our menu
  1132. // descriptor
  1133. //
  1134. bool
  1135. TFrameWindow::RestoreMenu()
  1136. {
  1137.   if (!MenuDescr)
  1138.     return false;
  1139.  
  1140.   HMENU curMenu = GetMenu();
  1141.   TMenu newMenu(*MenuDescr, NoAutoDelete);
  1142.   if (SetMenu(newMenu)) {
  1143.     MergeModule = 0;
  1144.     ::DestroyMenu(curMenu);
  1145.   }
  1146.   else
  1147.     ::DestroyMenu(newMenu);
  1148.   return true;
  1149. }
  1150.  
  1151.  
  1152. #endif
  1153. #if !defined(SECTION) || SECTION == 3
  1154.  
  1155. IMPLEMENT_STREAMABLE1(TFrameWindow, TWindow);
  1156.  
  1157. #if !defined(BI_NO_OBJ_STREAMING)
  1158.  
  1159. //
  1160. // Reads data of the uninitialized TFrameWindow from the passed ipstream
  1161. //
  1162. void*
  1163. TFrameWindow::Streamer::Read(ipstream& is, uint32 version) const
  1164. {
  1165.   TFrameWindow* o = GetObject();
  1166.   ReadVirtualBase((TWindow*)o, is);
  1167.   if (o->IsFlagSet(wfMainWindow))
  1168.     return o;
  1169.  
  1170.   is >> o->ClientWnd;
  1171.   is >> o->KeyboardHandling;
  1172.   o->HWndRestoreFocus = 0;
  1173.  
  1174.   bool hasMenuDescr = is.readByte();
  1175.   if (hasMenuDescr) {
  1176.     o->MenuDescr = new TMenuDescr;
  1177.     is >> *o->MenuDescr;
  1178.   }
  1179.   else
  1180.     o->MenuDescr = 0;
  1181.  
  1182.   // stream in window icon information
  1183.   //
  1184.   is >> o->IconModule;
  1185.   is >> o->IconResId;
  1186.   if (version > 1) {
  1187.     is >> o->IconSmModule;
  1188.     is >> o->IconSmResId;
  1189.   }
  1190.   else {
  1191.     o->IconSmModule = 0;
  1192.     o->IconSmResId = 0;
  1193.   }
  1194.  
  1195.   // load the window's icons
  1196.   //
  1197.   o->CurIcon   = 0;
  1198.   o->CurIconSm = 0;
  1199.   o->SetIcon(o->IconModule, o->IconResId);
  1200.   o->SetIconSm(o->IconSmModule, o->IconSmResId);
  1201.  
  1202.   is >> o->MergeModule;
  1203.   is >> o->MinimizedPos;
  1204.  
  1205.   return o;
  1206. }
  1207.  
  1208. //
  1209. // Writes data of the TFrameWindow to the passed opstream
  1210. //
  1211. void
  1212. TFrameWindow::Streamer::Write(opstream& os) const
  1213. {
  1214.   TFrameWindow* o = GetObject();
  1215.   WriteVirtualBase((TWindow*)o, os);
  1216.   if (o->IsFlagSet(wfMainWindow))
  1217.     return;
  1218.  
  1219.   os << o->ClientWnd;
  1220.   os << o->KeyboardHandling;
  1221.  
  1222.   os.writeByte(uint8(o->MenuDescr ? 1 : 0));
  1223.   if (o->MenuDescr)
  1224.     os << *o->MenuDescr;
  1225.  
  1226.   os << o->IconModule;
  1227.   os << o->IconResId;
  1228.   os << o->IconSmModule;      // added in stream ver 2
  1229.   os << o->IconSmResId;       // added in stream ver 2
  1230.  
  1231.   os << o->MergeModule;
  1232.   WINDOWPLACEMENT windata;
  1233.   windata.length = sizeof(WINDOWPLACEMENT);
  1234.   o->GetWindowPlacement(&windata);
  1235.   os << TPoint(windata.ptMinPosition);
  1236. }
  1237.  
  1238. #endif  // if !defined(BI_NO_OBJ_STREAMING)
  1239.  
  1240. #endif  // SECTION
  1241.